package com.cn.web.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.crypto.SmUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cn.error.ErrorConstantUtil;
import com.cn.exception.MyException;
import com.cn.group.GroupDO;
import com.cn.openapp.OpenEventDataItemTypeEnum;
import com.cn.org.OrgDO;
import com.cn.page.PageVo;
import com.cn.pwd.util.PwdCheckUtil;
import com.cn.pwd.vo.UserVo;
import com.cn.redis.RedisHandleUtil;
import com.cn.role.RoleDO;
import com.cn.user.UserDO;
import com.cn.user.UserGroupDO;
import com.cn.user.UserRoleDO;
import com.cn.user.UserUnitDO;
import com.cn.util.*;
import com.cn.web.role.service.UserRoleService;
import com.cn.web.user.mapper.IUserMapper;
import com.cn.web.user.service.UserGroupService;
import com.cn.web.user.service.UserService;
import com.cn.web.user.service.UserUnitService;
import com.cn.web.user.vo.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * description: UserService <br>
 * date: 21.12.14 13:42 <br>
 * author: cn_yaojin <br>
 * version: 1.0 <br>
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService, ApplicationRunner {

    @Autowired
    private IUserMapper userMapper;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private UserUnitService userUnitService;

    @Autowired
    private UserGroupService userGroupService;


    private String getIdentifyUserName(String data) {
        return PassRandomStrUtil.getPass();
    }

    public static String md5(String data) {
        return SmUtil.sm3(data);
    }

    /**
     * 编辑用户
     *
     * @param userEdit
     */
    @Override
    @Transactional
    public Object edit(UserEditDTO userEdit) {
        String ps = null;
        UserDO token = AuthUserContext.get();
        if (token.isChildAccount()) {
            throw new MyException("子账号禁止编辑");
        }
        UserDO entity = new UserDO();
        BeanUtils.copyProperties(userEdit, entity);
        entity
                .setAccountName(userEdit.getPhone())
                .setAddId(token.getId().toString())
                .setAddAccount(token.getUserName())
                .setAddName(token.getAddName());
        //判断账号是否已经存在
        if (entity.getId() == null || entity.getId() <= 0) {
            //校验用户是否已经存在
            checkPhone(userEdit, false, 1);
            String source_pwd = getIdentifyUserName(entity.getUserName());
            String pwd = md5(source_pwd);
            if (StringUtils.isNotEmpty(pwd)) {
                entity.setPassword(pwd);
            }
            entity.setId(SnowflakeIdWorkerUtil.getId());
            entity.setUserSlotId(incrementUserSlotId().intValue());
            entity.setUserType(1);//内部管理员才有新增，其他用户均是正常途径注册
            this.userMapper.insert(entity);
            ps = source_pwd;

        } else {
            //校验用户是否已经存在
            checkPhone(userEdit, true, 1);
            this.userMapper.update(entity);
        }

        //处理角色
        handleRoles(userEdit.getRoleIds(), entity.getId().toString());
        //处理单位
        handleUnits(userEdit.getOrgIds(), entity.getId().toString());
        //处理分组
        Set<String> groupIds = new HashSet<>();
        var groups = userEdit.getGroupId();
        if (groups != null) {
            for (GroupDO group : groups) {
                groupIds.add(group.getId().toString());
            }
        }
        handleGroups(groupIds, entity.getId().toString());

        //推送事件
        sendUser2Event(entity.getId().toString(), OpenEventDataItemTypeEnum.update.name());

        return ps;
    }

    public void sendUser2Event(String id, String type) {
        UserDO userDO = this.findById(id);
        userDO.setRoles(null).setOrgs(null).setMenuPermission(null).setPassword(null)
                .setAddAccount(null).setAddId(null).setAddName(null).setVersionFlag(null)
        ;
    }

    /**
     * 校验手机号、用户名是否已经存在
     *
     * @param edit
     * @param flag
     */
    private void checkPhone(UserEditDTO edit, boolean flag, int type) {
        QueryWrapper<UserDO> query = new QueryWrapper<>();
        //手机号
        if (StringUtils.isNotEmpty(edit.getPhone())) {
            query.eq("phone", edit.getPhone());
            query.ne("user_status", "2");
            query.eq("user_type", type);
            if (edit.getId() != null && edit.getId() > 0) {
                query.ne("id", edit.getId());
            }
            long count = this.userMapper.selectCount(query);
            if (count > 0) {
                throw new MyException(ErrorConstantUtil.PHONE_EXSIT_ERROR);
            }
        }
    }

    @Override
    public List<UserVO> list(UserSearch userSearch) {
        var data = this.userMapper.list(userSearch);
        if (data != null && data.size() > 0) {
            for (UserVO t : data) {
                //脱敏
//                t.setCardNum(DesensitizedUtil.idCardNum(t.getCardNum(), 3, 2));
//                t.setPhone(DesensitizedUtil.mobilePhone(t.getPhone()));

                t.setUserId(t.getId().toString());
                t.setRoles(listRole(t.getUserId()));
                t.setUnits(listOrg(t.getUserId()));
                t.setGroups(listGroup(t.getUserId()));
            }
        }
        return data;
    }

    @Override
    public PageInfo<UserVO> page(PageVo<UserSearch> pageVo) {
        return PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize()).doSelectPageInfo(() -> {
            list(pageVo.getParam());
        });
    }

    @Override
    public Object searchUser(UserSearch userSearch) {
        var data = this.userMapper.list(userSearch);
        List<Map<String, String>> list = new ArrayList<>();
        for (UserVO t : data) {
            Map<String, String> tmp = new HashMap<>();
            tmp.put("id", t.getId().toString());
            tmp.put("name", t.getUserName());
            list.add(tmp);
        }
        return list;
    }

    @Override
    public UserEditDTO find(String userId) {
        UserDO user = findById(userId);
        UserEditDTO edit = new UserEditDTO();
        BeanUtils.copyProperties(user, edit);
        edit.setRoleIds(listRole(userId)).setOrgIds(listOrg(userId))
                .setUserId(userId).setGroupId(listGroup(userId))
                .setAdminFlag(user.getSuperFlag());
        return edit;
    }

    @Override
    public UserEditDTO findByPhone(String phone) {
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        queryWrapper.eq("user_status", 0);
        UserDO user = this.userMapper.selectOne(queryWrapper);
        UserEditDTO edit = new UserEditDTO();
        BeanUtils.copyProperties(user, edit);
        return edit;
    }

    @Override
    public UserDO findById(String id) {
        var data = this.userMapper.selectById(id);
        if (data == null) {
            throw new MyException(ErrorConstantUtil.USER_NO_EXSIT);
        }
        return data;
    }

    @Override
    public UserDO findUserDoByPhone(String phone, String userType) {
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        queryWrapper.eq("user_type", userType);
        queryWrapper.ne("user_status", 2);
        var list = this.userMapper.selectList(queryWrapper);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        throw new MyException(ErrorConstantUtil.USER_NO_EXSIT);
    }

    @Override
    public void start(Long userId, String status) {
        if (userId.toString().equalsIgnoreCase("1")) {
            throw new MyException("初始账户禁止操作！");
        }
        UserDO user = new UserDO();
        user.setId(userId);
        user.setUserStatus(status);
        this.userMapper.update(user);
        if (status.equalsIgnoreCase("1")) {
            reshAuth(userId.toString());
        }
    }

    private void reshAuth(String userId) {
        //强制下线
        try {
            StpUtil.logout(userId);
        } catch (Exception e) {

        }
    }

    @Override
    @Transactional
    public void del(Long userId) {
        start(userId, "2");
        //删除分配给他的权限
        userUnitService.del(userId.toString());
        userRoleService.del(userId.toString());
        //强制下线
        reshAuth(userId.toString());

    }

    @Override
    public String restPass(Long userId) {
        var t = this.userMapper.selectById(userId);
        if (t == null) {
            throw new MyException(ErrorConstantUtil.USER_NO_EXSIT);
        }
        String identifyUserName = getIdentifyUserName(t.getUserName());
        t.setPassword(md5(identifyUserName));
        this.userMapper.updateById(t);
        return identifyUserName;
    }

    @Override
    public void updatePass(PassRestEditDTO pass) {
        if (!pass.getNewPass().equals(pass.getConfirmPass())) {
            throw new MyException(ErrorConstantUtil.PASS_WORD_CONFIRM_ERROR);
        }
        if (pass.getOldPass().equals(pass.getNewPass())) {
            throw new MyException(ErrorConstantUtil.PASS_NEWPASS_CONFIRM_ERROR);
        }
        UserDO token = AuthUserContext.get();
        if (token.isChildAccount()) {
            throw new MyException("子账号禁止编辑");
        }


        //校验规则
        //校验密码复杂度
        UserVo passUser = new UserVo();
        passUser.setAccount(token.getAccountName());//账号
        passUser.setPhone(token.getPhone());//手机号
        if (StringUtils.isNotEmpty(token.getUserName())) {
            //姓名不为空
            passUser.setUserName(token.getUserName());
        }
        PwdCheckUtil.evalPwd(pass.getNewPass(), passUser);

        UserDO data = this.userMapper.selectById(token.getId());
        if (data == null) {
            throw new MyException(ErrorConstantUtil.USER_NO_EXSIT);
        }
        if (!data.getPassword().equalsIgnoreCase(SmUtil.sm3(pass.getOldPass()))) {
            throw new MyException(ErrorConstantUtil.PASS_OLDPASS_CONFIRM_ERROR);
        }
        data.setPassword(md5(pass.getNewPass()));
        this.userMapper.updateById(data);
    }

    @Override
    public void updateUserBaseInfoById(OpenUserEdit info) {
        var data = this.userMapper.selectById(info.getId());
        if (data == null) {
            throw new MyException(ErrorConstantUtil.USER_NO_EXSIT);
        }

        data.setUserName(info.getUserName())
                .setNickName(info.getNickName())
                .setGender(info.getGender())
                .setNation(info.getNation())
                .setEdu(info.getEdu())
                .setSchool(info.getSchool())
                .setEmail(info.getEmail())
                .setCardNum(info.getCardNum())
                .setPhoto(info.getPhoto())
        ;
        if (StringUtils.isNotEmpty(info.getValidateFlag())) {
            data.setValidateFlag(info.getValidateFlag());
        }
        //更新用户基础信息
        this.userMapper.updateById(data);


    }

    @Override
    public PageInfo<UserEditDTO> pageUserPageOpenApi(PageVo<UserSearch> pageVo) {
        return PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize()).doSelectPageInfo(() -> {
            listUsersOpenApi(pageVo.getParam());
        });
    }

    public List<UserEditDTO> listUsersOpenApi(UserSearch search) {
        var data = this.userMapper.listUsersOpenApi(search);
        if (data != null) {
            for (UserEditDTO t : data) {
                t.setCardNum(null);
            }
        }
        return data;
    }


    @Override
    public List<UserDO> selectPhone(List<String> userIds) {
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", userIds);
        var data = this.userMapper.selectList(queryWrapper);
        return data;
    }

    @Override
    public Object logout() {
        UserDO token = AuthUserContext.get();
        if (token.isChildAccount()) {
            throw new MyException("子账号禁止操作");
        }
        var user = this.userMapper.selectById(token.getId());
        user.setUserStatus("2");//外部用户，注销就是删除
        this.userMapper.updateById(user);
        //清空登录信息
        StpUtil.getSession().clear();
        StpUtil.logout(token.getId());
        return null;
    }

    public List<GroupDO> listGroup(String userId) {
        var data = this.userGroupService.listByUserId(userId);
        return data;
    }

    public List<UserVO.UserRoles> listRole(String userId) {
        var data = this.userMapper.listRole(userId);
        List<UserVO.UserRoles> tmp = new ArrayList<>();
        for (RoleDO datum : data) {
            tmp.add(new UserVO.UserRoles().setId(datum.getId().toString()).setRoleName(datum.getRoleName()));
        }
        return tmp;
    }

    public List<UserVO.UserUnits> listOrg(String userId) {
        var data = this.userMapper.listOrg(userId);
        List<UserVO.UserUnits> tmp = new ArrayList<>();
        for (OrgDO org : data) {
            tmp.add(new UserVO.UserUnits().setId(org.getId().toString()).setOrgName(org.getOrgName()));
        }
        return tmp;
    }

    private void handleGroups(Set<String> groupIds, String userId) {
        if (groupIds == null || groupIds.size() == 0) {
            this.userGroupService.delByUserId(userId);
        } else {
            //查询旧的
            List<UserGroupDO> groups = this.userGroupService.selectUserGroups(userId);
            Map<String /*groupId*/, UserGroupDO> map = new HashMap<>();
            for (UserGroupDO group : groups) {
                map.put(group.getGroupId(), group);
            }

            //待删除的
            Set<String> delIds = new HashSet<>();

            //遍历旧的， 判断旧的是否在新的中
            for (String s : map.keySet()) {
                if (!groupIds.contains(s)) {
                    delIds.add(map.get(s).getId().toString());
                }
            }
            this.userGroupService.delByIds(delIds);
            delIds.clear();

            //遍历新的，判断新的是否在旧的中
            for (String groupId : groupIds) {
                if (map.keySet().contains(groupId)) {
                    continue;
                }
                delIds.add(groupId);
            }
            this.userGroupService.addGroupIds(delIds, userId);
        }
    }

    private void handleUnits(List<UserVO.UserUnits> units, String userId) {
        if (units != null) {
            units.forEach(t -> {
                t.setOrgName("");
            });
        }
        //原来的单位
        List<UserVO.UserUnits> old = listOrg(userId);
        for (UserVO.UserUnits t : old) {
            t.setOrgName("");
        }
        //有变动
        if (!GsonUtil.toJson(old).equalsIgnoreCase(GsonUtil.toJson(units))) {
            //删除用户拥有的角色
            userUnitService.del(userId);
            //重新分配角色
            if (units != null) {
                UserUnitDO entity = new UserUnitDO();
                for (UserVO.UserUnits role : units) {
                    entity.setUnitId(role.getId()).setUserId(userId);
                    this.userUnitService.add(entity);
                }
            }
        }
    }

    public void handleRoles(List<UserVO.UserRoles> roles, String userId) {
        if (roles != null) {
            roles.forEach(t -> {
                t.setRoleName("");
            });
        }
        //原来的角色
        List<UserVO.UserRoles> old = listRole(userId);
        for (UserVO.UserRoles t : old) {
            t.setRoleName("");
        }
        //有变动
        if (!GsonUtil.toJson(old).equalsIgnoreCase(GsonUtil.toJson(roles))) {
            //删除用户拥有的角色
            userRoleService.del(userId);

            //重新分配角色
            if (roles != null) {
                UserRoleDO userRoleEntity = new UserRoleDO();
                for (UserVO.UserRoles role : roles) {
                    userRoleEntity.setRoleId(role.getId()).setUserId(userId);
                    this.userRoleService.add(userRoleEntity);
                }
            }
        }
    }


    @Override
    public void run(ApplicationArguments args) throws Exception {
        String userSlotId = getUserSlotId();
        Long value = getMaxUserSlot();
        if (StringUtils.isEmpty(userSlotId)) {
            if (value != null) {
                //同步到缓存中
                RedisHandleUtil.increment(getUserSlotKey(), value);
            }
        } else {
            if (value != null) {
                Long userId = Long.valueOf(userSlotId);
                if (userId.compareTo(value) < 0) {
                    RedisHandleUtil.increment(getUserSlotKey(), (value.longValue() - userId.longValue()));
                } else {
                    log.info("数据库与缓存中slotId比较完成,redis's value:" + userSlotId + ",mysql's value:" + value);
                }
            }
        }
    }

    private static String getUserSlotKey() {
        return ConstanUserUtil.USER_SLOT_KEY;
    }

    public static String getUserSlotId() {
        return RedisHandleUtil.get(getUserSlotKey());
    }

    public static Long incrementUserSlotId() {
        return RedisHandleUtil.increment(getUserSlotKey(), 1L);
    }

    public Long getMaxUserSlot() {
        return this.userMapper.getMaxUserSlot(null);
    }

    public Long checkUserExist(Object pd) {
        return this.userMapper.checkUserExist(pd);
    }
}
